# 产品归类逻辑实现

import pandas as pd
import logging
from src.config.constants import (
    PACKAGE_TYPE,
    CLASSIFICATION_CATEGORIES,
    MATCH_STATUS,
    FIELD_NAMES
)


def _determine_product_category(matched_historical):
    """确定产品归类

    根据项目大纲2.3.1.2节定义：依据规格型号所具备的历史包装种类，将待处理数据划分为以下三类：
    - 仅组合包装：历史数据中，该规格型号仅存在组合包装类型。
    - 仅独立包装：历史数据中，该规格型号仅存在独立包装类型。
    - 混合包装：历史数据中，该规格型号同时存在独立包装类型和组合包装类型。

    Args:
        matched_historical: 匹配的历史记录

    Returns:
        产品归类（仅组合包装、仅独立包装、混合包装）
    """
    # 初始化logger
    logger = logging.getLogger("product_classification")
    
    # 数据有效性验证
    if matched_historical is None or matched_historical.empty:
        logger.warning("匹配的历史记录为空，返回默认分类")
        return "混合包装"
    
    # 检查是否包含包装类型列
    if "包装类型" not in matched_historical.columns:
        logger.warning("历史记录中不包含'包装类型'列，尝试使用备用字段或返回默认分类")
        # 尝试使用备用字段（使用get方法避免KeyError）
        package_type_field = FIELD_NAMES.get("PACKAGE_TYPE")
        if package_type_field and package_type_field in matched_historical.columns:
            package_type_column = package_type_field
        else:
            logger.warning("无法找到包装类型相关字段，返回默认分类")
            return "混合包装"
    else:
        package_type_column = "包装类型"
    
    # 去除NaN值并获取唯一包装类型
    try:
        # 提取非空的包装类型
        non_null_package_types = matched_historical[package_type_column].dropna()
        if non_null_package_types.empty:
            logger.warning("所有包装类型值都为空，返回默认分类")
            return "混合包装"
        
        package_types = set(non_null_package_types.unique())
        
        # 日志记录找到的包装类型
        logger.debug(f"找到的包装类型: {package_types}")
        
        # 检查是否包含组合包装和独立包装
        has_combination = PACKAGE_TYPE["COMBINATION"] in package_types
        has_independent = PACKAGE_TYPE["INDEPENDENT"] in package_types
        
        # 确定产品归类
        if has_combination and not has_independent:
            logger.debug("产品归类为：仅组合包装")
            return "仅组合包装"
        elif has_independent and not has_combination:
            logger.debug("产品归类为：仅独立包装")
            return "仅独立包装"
        elif has_combination and has_independent:
            logger.debug("产品归类为：混合包装")
            return "混合包装"
        else:
            # 如果没有找到预定义的包装类型，返回混合包装
            logger.warning(f"未找到预定义的包装类型，当前包装类型: {package_types}")
            return "混合包装"
    except Exception as e:
        logger.error(f"确定产品归类时发生错误: {str(e)}")
        return "混合包装"


def _copy_package_info(target, source_record, index=None):
    """复制包装方案信息

    Args:
        target: 目标数据结构（DataFrame或Series）
        source_record: 源记录
        index: 可选，目标DataFrame中的索引位置
    """
    # 初始化logger
    logger = logging.getLogger("package_info_copy")
    
    # 数据有效性验证
    if target is None:
        logger.warning("目标数据结构为空，无法复制包装信息")
        return
    
    if source_record is None:
        logger.warning("源记录为空，无法复制包装信息")
        return
    
    # 定义需要复制的包装相关字段映射
    package_fields_mapping = {
        "包装类型": FIELD_NAMES.get("PACKAGE_TYPE", "包装类型"),
        "包装材料": FIELD_NAMES.get("PACKAGE_MATERIAL", "包装材料"),
        "包装尺寸": FIELD_NAMES.get("PACKAGE_SIZE", "包装尺寸"),
        "单位包装数量": FIELD_NAMES.get("PACKAGE_QUANTITY", "单位包装数量"),
        "包装成本": FIELD_NAMES.get("PACKAGE_COST", "包装成本"),
    }
    
    copied_fields = []
    missing_fields = []
    
    if isinstance(target, pd.DataFrame):
        # 处理DataFrame情况
        if index is not None and index not in target.index:
            logger.warning(f"指定的索引 {index} 不在目标DataFrame中")
            return
        
        target_index = index if index is not None else target.index[0]
        
        for field_alias, field_name in package_fields_mapping.items():
            # 检查源记录中是否存在该字段
            if isinstance(source_record, pd.Series):
                field_exists = field_name in source_record.index
            elif isinstance(source_record, dict):
                field_exists = field_name in source_record
            else:
                field_exists = hasattr(source_record, field_name)
            
            if field_exists:
                # 获取源记录中的字段值
                if isinstance(source_record, pd.Series):
                    field_value = source_record[field_name]
                elif isinstance(source_record, dict):
                    field_value = source_record[field_name]
                else:
                    field_value = getattr(source_record, field_name)
                
                # 检查目标DataFrame中是否存在该字段，如果不存在则创建
                if field_name not in target.columns:
                    target[field_name] = None
                
                # 复制字段值
                target.at[target_index, field_name] = field_value
                copied_fields.append(field_name)
            else:
                missing_fields.append(field_name)
    elif isinstance(target, pd.Series):
        # 处理Series情况
        for field_alias, field_name in package_fields_mapping.items():
            # 检查源记录中是否存在该字段
            if isinstance(source_record, pd.Series):
                field_exists = field_name in source_record.index
            elif isinstance(source_record, dict):
                field_exists = field_name in source_record
            else:
                field_exists = hasattr(source_record, field_name)
            
            if field_exists:
                # 获取源记录中的字段值
                if isinstance(source_record, pd.Series):
                    field_value = source_record[field_name]
                elif isinstance(source_record, dict):
                    field_value = source_record[field_name]
                else:
                    field_value = getattr(source_record, field_name)
                
                # 复制字段值
                target[field_name] = field_value
                copied_fields.append(field_name)
            else:
                missing_fields.append(field_name)
    else:
        logger.warning(f"不支持的目标数据类型: {type(target)}")
        return
    
    # 记录复制结果
    if copied_fields:
        logger.debug(f"成功复制的字段: {', '.join(copied_fields)}")
    if missing_fields:
        logger.debug(f"源记录中缺少的字段: {', '.join(missing_fields)}")


def _find_closest_quantity_record(records, target_quantity):
    """查找数量最接近的记录

    Args:
        records: 记录列表
        target_quantity: 目标数量

    Returns:
        数量最接近的记录
    """
    # 初始化logger
    logger = logging.getLogger("closest_quantity")
    
    # 数据有效性验证
    if records is None or records.empty:
        logger.warning("记录列表为空，无法查找最接近数量的记录")
        return None
    
    if target_quantity is None or pd.isna(target_quantity):
        logger.warning("目标数量为空或无效，返回第一条记录")
        return records.iloc[0] if not records.empty else None
    
    try:
        # 尝试将目标数量转换为数值类型
        target_quantity = float(target_quantity)
    except (ValueError, TypeError):
        logger.warning(f"无法将目标数量 '{target_quantity}' 转换为数值类型，返回第一条记录")
        return records.iloc[0] if not records.empty else None
    
    # 检查记录中是否包含数量字段
    quantity_field = FIELD_NAMES.get("QUANTITY", "数量")
    if quantity_field not in records.columns:
        logger.warning(f"记录中不包含数量字段 '{quantity_field}'，无法查找最接近数量的记录")
        return records.iloc[0] if not records.empty else None
    
    # 创建记录的副本以避免修改原始数据
    records_copy = records.copy()
    
    try:
        # 确保数量字段为数值类型
        records_copy[quantity_field] = pd.to_numeric(records_copy[quantity_field], errors='coerce')
        
        # 过滤掉数量字段为空的记录
        valid_records = records_copy.dropna(subset=[quantity_field])
        
        if valid_records.empty:
            logger.warning("没有有效的数量记录，返回第一条记录")
            return records.iloc[0] if not records.empty else None
        
        # 计算每条记录与目标数量的差值绝对值
        valid_records["quantity_diff"] = abs(valid_records[quantity_field] - target_quantity)
        
        # 按差值排序，选择差值最小的记录
        closest_record = valid_records.sort_values(by="quantity_diff").iloc[0]
        
        logger.debug(f"找到数量最接近的记录，目标数量: {target_quantity}，记录数量: {closest_record[quantity_field]}, 差值: {closest_record['quantity_diff']}")
        
        return closest_record
    except Exception as e:
        logger.error(f"查找最接近数量记录时发生错误: {str(e)}")
        # 发生错误时返回第一条记录
        return records.iloc[0] if not records.empty else None


def _estimate_design_parameters(data):
    """为设计项估算包装参数

    Args:
        data: 待处理的数据

    Returns:
        处理后的数据
    """
    # 初始化logger
    logger = logging.getLogger("design_parameters")
    
    # 数据有效性验证
    if data is None or data.empty:
        logger.warning("待处理数据为空，无法估算设计参数")
        return data
    
    # 创建数据副本以避免修改原始数据
    result_data = data.copy()
    
    # 检查是否包含备注字段
    if "备注" not in result_data.columns:
        logger.warning("数据中不包含'备注'字段，无法识别设计项")
        return result_data
    
    # 筛选设计项
    design_mask = result_data["备注"] == MATCH_STATUS["DESIGN"]
    design_count = design_mask.sum()
    
    if design_count == 0:
        logger.debug("没有找到设计项，无需估算包装参数")
        return result_data
    
    logger.info(f"找到 {design_count} 个设计项，开始估算包装参数")
    
    # 为每个设计项估算包装参数
    for idx in result_data[design_mask].index:
        try:
            # 获取相关信息
            specification = result_data.loc[idx, FIELD_NAMES.get("SPECIFICATION_MODEL", "规格型号")]
            quantity = result_data.loc[idx, FIELD_NAMES.get("QUANTITY", "数量")]
            
            # 初始化估算结果字典
            estimates = {
                "设计建议": "需要新设计包装方案",
                "估算依据": "基于数量和规格型号的简单估算"
            }
            
            # 基于数量估算包装数量
            if pd.notna(quantity):
                try:
                    quantity_value = float(quantity)
                    if quantity_value > 0:
                        # 根据不同数量级别使用不同的估算策略
                        if quantity_value <= 100:
                            # 小件物品，每箱装10个
                            estimated_boxes = max(1, int(quantity_value / 10))
                        elif quantity_value <= 1000:
                            # 中等数量，每箱装20个
                            estimated_boxes = max(1, int(quantity_value / 20))
                        else:
                            # 大量物品，每箱装50个
                            estimated_boxes = max(1, int(quantity_value / 50))
                        estimates["估算包装数量"] = estimated_boxes
                        estimates["单位包装数量"] = 10 if quantity_value <= 100 else 20 if quantity_value <= 1000 else 50
                except (ValueError, TypeError):
                    logger.warning(f"记录 {idx} 的数量值 '{quantity}' 无效，无法估算包装数量")
            
            # 基于规格型号估算包装类型
            if pd.notna(specification):
                spec_str = str(specification).lower()
                # 根据规格型号中的关键词判断可能的包装类型
                if any(keyword in spec_str for keyword in ["大", "重", "箱", "盒"]):
                    estimates["建议包装类型"] = "纸箱"
                elif any(keyword in spec_str for keyword in ["小", "轻", "袋"]):
                    estimates["建议包装类型"] = "塑料袋"
                elif any(keyword in spec_str for keyword in ["瓶", "罐", "液体"]):
                    estimates["建议包装类型"] = "防摔包装"
                else:
                    estimates["建议包装类型"] = "标准包装"
            
            # 将估算结果写入数据
            for key, value in estimates.items():
                if key not in result_data.columns:
                    result_data[key] = None
                result_data.loc[idx, key] = value
                
        except Exception as e:
            logger.error(f"为记录 {idx} 估算包装参数时发生错误: {str(e)}")
    
    logger.info(f"设计项包装参数估算完成，共处理 {design_count} 个设计项")
    return result_data


def classify_products(matched_data):
    """对产品进行归类

    Args:
        matched_data: 已匹配的数据

    Returns:
        分类后的产品数据
    """
    # 初始化logger
    logger = logging.getLogger("product_classification")
    
    # 数据有效性验证
    if matched_data is None or matched_data.empty:
        logger.warning("已匹配的数据为空，无法进行产品归类")
        return matched_data
    
    # 创建数据副本以避免修改原始数据
    result = matched_data.copy()
    
    # 检查必需的字段
    required_fields = ["备注", "包装类型", "匹配历史记录"]
    for field in required_fields:
        if field not in result.columns:
            logger.warning(f"数据中不包含必需字段 '{field}'，尝试使用备用字段或创建空字段")
            if field == "备注" and FIELD_NAMES.get("MATCH_STATUS") in result.columns:
                result["备注"] = result[FIELD_NAMES["MATCH_STATUS"]]
            elif field == "包装类型" and FIELD_NAMES.get("PACKAGE_TYPE") in result.columns:
                result["包装类型"] = result[FIELD_NAMES["PACKAGE_TYPE"]]
            elif field == "匹配历史记录":
                result["匹配历史记录"] = None
            else:
                result[field] = None
    
    # 初始化分类统计
    classification_stats = {
        "设计项": 0,
        "仅组合包装": 0,
        "仅独立包装": 0,
        "混合包装": 0,
        "未分类": 0
    }
    
    # 遍历每条记录进行分类
    for idx, row in result.iterrows():
        try:
            # 检查是否为设计项
            if row["备注"] == MATCH_STATUS["DESIGN"]:
                classification_stats["设计项"] += 1
                continue
            
            # 获取产品类别和匹配历史记录
            product_category = row["包装类型"]
            historical_records = row["匹配历史记录"]
            
            # 处理产品归类
            if product_category == "仅组合包装":
                # 归类为仅组合包装，标记为参考项（仅组合包装）
                result.at[idx, "备注"] = CLASSIFICATION_CATEGORIES["REFERENCE_COMBINATION"]
                classification_stats["仅组合包装"] += 1
                logger.debug(f"记录 {idx} 归类为仅组合包装")
            elif product_category == "仅独立包装":
                # 仅独立包装，需要进一步处理
                try:
                    from .iterative_splitting import _process_independent_package
                    _process_independent_package(result, idx, row, historical_records)
                    classification_stats["仅独立包装"] += 1
                    logger.debug(f"记录 {idx} 归类为仅独立包装")
                except ImportError:
                    logger.error("无法导入独立包装处理模块，标记为未分类")
                    result.at[idx, "备注"] = "未分类"
                    classification_stats["未分类"] += 1
                except Exception as e:
                    logger.error(f"处理独立包装时发生错误: {str(e)}")
                    result.at[idx, "备注"] = "未分类"
                    classification_stats["未分类"] += 1
            elif product_category == "混合包装":
                # 混合包装，需要进一步处理
                try:
                    from .iterative_splitting import _process_mixed_package
                    _process_mixed_package(result, idx, row, historical_records)
                    classification_stats["混合包装"] += 1
                    logger.debug(f"记录 {idx} 归类为混合包装")
                except ImportError:
                    logger.error("无法导入混合包装处理模块，标记为未分类")
                    result.at[idx, "备注"] = "未分类"
                    classification_stats["未分类"] += 1
                except Exception as e:
                    logger.error(f"处理混合包装时发生错误: {str(e)}")
                    result.at[idx, "备注"] = "未分类"
                    classification_stats["未分类"] += 1
            else:
                # 无法识别的产品类别
                logger.warning(f"无法识别的产品类别 '{product_category}'，标记为未分类")
                result.at[idx, "备注"] = "未分类"
                classification_stats["未分类"] += 1
        except Exception as e:
            logger.error(f"对记录 {idx} 进行分类时发生错误: {str(e)}")
            result.at[idx, "备注"] = "分类错误"
            classification_stats["未分类"] += 1
    
    # 记录分类统计结果
    logger.info(f"产品归类完成，统计结果: {classification_stats}")
    
    return result
